#include <windows.h>
#include "cvhelper.h"

#include <iostream>
#include <fstream>

/*
* 将一幅RGB图像转换YCrCb图像，并得到三个分离的通道。
*/
void TransferRGB2YCrCb(cv::Mat &input, std::vector<cv::Mat> &chls)
{
    //YCrCb色彩空间：Y为颜色的亮度（luma）成分、而CB和CR则为蓝色和红色的浓度偏移量成分。
    //只有Y成分的图基本等同于彩色图像的灰度图。
    cv::cvtColor(input, input, cv::COLOR_BGR2YCrCb);
    cv::split(input, chls);
}

/*
* 将Y、Cr、Cb三个分离的通道合并为YCrCb图像，再转换为RGB图像。
*/
void MergeYCrCb2RGB(cv::Mat &output, std::vector<cv::Mat> &chls)
{
    cv::merge(chls, output);
    cv::cvtColor(output, output, cv::COLOR_YCrCb2BGR);
}


/*
*CLAHE.
*@ClipLimit:直方图的柱子高度大于计算后的ClipLimit的部分被裁剪掉，然后将其平均分配给整张直方图,从而提升*整个图像。
*但是本人觉得这里解释的不对，更像是累计概率分布函数的斜率上限阈值，高于ClipLimit的部分被裁剪掉，
*然后将其平均分配给整张直方图,从而提升整个图像。
*@TilesGridSize:// 将图像分为TilesGridSize*TilesGridSize大小的方块.。（滑动窗口大小为TilesGridSize*TilesGridSize）
*/
cv::Mat clahe_deal(cv::Mat &src, double ClipLimit = 40.0, int TilesGridSize = 8)
{
    cv::Mat ycrcb = src.clone();
    std::vector<cv::Mat> channels;

//    TransferRGB2YCrCb(ycrcb, channels);

//    cv::Mat clahe_img;
//    //opencv源码中原型：  createCLAHE(double clipLimit = 40.0, Size tileGridSize = Size(8, 8));
//    cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE();

//    clahe->setClipLimit(ClipLimit);
//    clahe->setTilesGridSize(Size(TilesGridSize, TilesGridSize));
//    clahe->apply(channels[0], clahe_img);
//    channels[0].release();
//    clahe_img.copyTo(channels[0]);
//    MergeYCrCb2RGB(ycrcb, channels);
//    return ycrcb;
    return ycrcb;
}


//将mat数组转换为二进制，255用1表示，0用0表示
int  Mat2Binary(const  cv::Mat img, int  line_byte, char* data)
{
    int  width = img.cols;
    int  height = img.rows;
    size_t  line_size = line_byte * 8;
    size_t  bit_size = line_size * height;


    char* p = data;  int  offset, v; unsigned  char  temp;
    for (int row = height - 1; row >= 0; row--)
    {
        for (int col = 0; col < width; col++)
        {
            offset = col % 8;
            //v = img.data[row * width + col];
            v = img.at<uchar>(row, col);
            temp = 1;
            temp = temp << (8 - offset - 1);
            if (v == 255)
            {
                *(p + col / 8) |= temp;
            }
            else
            {
                temp = ~temp;
                *(p + col / 8) &= temp;
            }
        }
        //注释掉一下代码，列数否则不能被4整除的图像最后4列会出现黑边
/*		for (int j = width / 8; j < line_byte + 1; j++) {
            p[j] = 0;
        }	*/
        p = p + line_byte;
    }
    return  0;
}

//将Mat图像保存为1位bmp图
int  Save1BitImage(const  cv::Mat img, std::string dst)
{
    int  width = img.cols;
    int  height = img.rows;
    const  int  biBitCount = 1;

    //待存储图像数据每行字节数为4的倍数，计算位图每行占多少个字节
    //int  line_byte = (width * biBitCount >> 3 + 3) / 4 * 4;
    int  line_byte = (width * biBitCount / 8 + 5) / 4 * 4;//+3改为+5,否则无法向上对齐，造成崩溃
    char* p_data = (char*)malloc(line_byte * height + 1);//后面加1否则会报heap错误
    //memset(p_data, 0x01, line_byte * height+1);

    //将mat数组转换为二进制，255用1表示，0用0表示
    Mat2Binary(img, line_byte, p_data);

    //bmp位图颜色表大小，以字节为单位，灰度图像颜色表为256*4字节，彩色图像颜色表大小为0,二值图为2*4
    int  color_type_num = 2;
    int  colorTablesize = color_type_num * sizeof(RGBQUAD);
    RGBQUAD* pColorTable = new  RGBQUAD[color_type_num];
    for (int i = 0; i < color_type_num; i++) {
        pColorTable[i].rgbBlue = i * 255;
        pColorTable[i].rgbRed = i * 255;
        pColorTable[i].rgbGreen = i * 255;
        pColorTable[i].rgbReserved = 0;
    }
    //申请位图文件头结构变量，填写文件头信息
    BITMAPFILEHEADER fileHead;
    fileHead.bfType = 0x4D42;   //bmp类型
    fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTablesize + line_byte * height;   //bfSize是图像文件4个组成部分之和,图文件的大小，以字节为单位
    fileHead.bfReserved1 = 0;//位图文件保留字，必须为0
    fileHead.bfReserved2 = 0;
    fileHead.bfOffBits = 54 + colorTablesize;               //bfOffBits是图像文件前3个部分所需空间之和,位图数据的起始位置

    //申请位图信息头结构变量，填写信息头信息
    BITMAPINFOHEADER head;
    head.biBitCount = biBitCount;//每个像素所需的位数，必须是1（双色），（29-30字节）4(16色），8(256色）16(高彩色)或24（真彩色）之一
    head.biCompression = BI_RGB; //位图压缩类型,必须为BI_RGB否则图片打开会有错误
    head.biHeight = height;//位图的高度，以像素为单位
    head.biWidth = width;
    //head.biSize = 40;
    head.biSize = sizeof(BITMAPINFOHEADER);//本结构所占用字节数
    head.biSizeImage = line_byte * height;//位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节)
    head.biPlanes = 1;//目标设备的级别，必须为1
    head.biXPelsPerMeter = 23622;//位图水平分辨率，每米像素数，分辨率设为600
    head.biYPelsPerMeter = 23622;//位图垂直分辨率，每米像素数
    head.biClrImportant = 0;//位图显示过程中重要的颜色数
    head.biClrUsed = 0;//位图实际使用的颜色表中的颜色数

    std::ofstream fp(dst.c_str(), std::ios::binary | std::ios::out);
    if (!fp.is_open()) {
        return  -1;
    }
    fp.write((char*)&fileHead, sizeof(BITMAPFILEHEADER)); //写文件头进文件
    //写位图信息头进内存
    fp.write((char*)&head, sizeof(BITMAPINFOHEADER));
    //颜色表，写入文件
    fp.write((char*)pColorTable, sizeof(RGBQUAD) * color_type_num);
    //写位图数据进文件pBmpBuf
    fp.write((char*)p_data, height * line_byte);
    fp.close();

    delete[]pColorTable;
    delete[]p_data;
    return  0;
}


